#include "Material.h"
#include "Files.h"
#include "SOIL.h"
#include "Level.h"
#include <regex>
#include <sstream>

using namespace std;
Texture::Texture(){}

Texture::Texture(std::string fName){

	FileBuffer file(fName);
	if (!file.Exists()){
		Engine::LogError("Cannot load texture: " + fName + " - using default instead.", Engine::errorType::TYPE_ERROR);
		fName = "materials/default.tga";
	}

	texture = SOIL_load_OGL_texture(file.GetDir().c_str() , SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_TEXTURE_REPEATS);

	//printf("SOIL loading error: '%s'\n", SOIL_last_result());
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

}

Texture::~Texture(){

}

GLuint Texture::GetTexture(){
	return texture;
}

struct TextureData{
	string diffuse;
	string normal;
	string specular;
	string height;
	string illum;
};

Material::Material(){}

Material::~Material(){
	World::sceneMaterials.remove(this);
}

Material::Material(ShaderProgram* program, list<std::string> props){
	shader = program;
	TextureData textureData;
	tilingMultipler = 1.0f;
	bool hasDiffuse = false;
	bool hasNormal = false;
	bool hasHeight = false;
	bool hasSpecular = false;
	bool hasIllum = false;
	masked = false;
	translucentcy = 1.0f;
	colour = Colour(1.0f, 1.0f, 1.0f, 1.0f);
	list<string> textures;
	list<string>::iterator it = props.begin();
	while (it != props.end()){
		// ignore commented lines
		regex re("\/\/(\s*.+$)");
		smatch match;
		if (regex_search(*it, match, re)){
			++it;
		}
		else{
			re = regex("(.*)\\=");
			if (regex_search(*it, match, re)){
				string valName = match[1];
				valName.erase(std::remove_if(valName.begin(), valName.end(), isspace), valName.end());
				re = regex("\\=\\s*(.*)");
				if (regex_search(*it, match, re)){
					string value = match[1].str();
					if (valName == "alphamask"){
						int val;
						istringstream(value) >> val;
						val = std::max(0, std::min(val, 1));
						masked = val;
					}
					else if(valName == "tiling"){
						float val = stof(value);
						tilingMultipler = val;
					}
					else if (valName == "translucent"){
						float val = stof(value);
						val = std::max(0.0f, std::min(val, 1.0f));
						translucentcy = val;
					}
					else if (valName == "diffuse"){
						hasDiffuse = true;
						FileBuffer file(value);
						if (!file.Exists()){
							Engine::LogError("Cannot find texture: "+value+" - using default.tga instead", Engine::errorType::TYPE_ERROR);
							value = "materials/default.tga";
						}
						textureData.diffuse = value;
					}
					else if (valName == "colour"){
						// todo
					}
					else if (valName == "normal"){
						FileBuffer file(value);
						if (!file.Exists()){
							Engine::LogError("Cannot find normal map: " + value, Engine::errorType::TYPE_WARNING);
						}
						else{
							hasNormal = true;
							textureData.normal = value;
						}
					}
					else if (valName == "height"){
						FileBuffer file(value);
						if (!file.Exists()){
							Engine::LogError("Cannot find height map: " + value, Engine::errorType::TYPE_WARNING);
						}
						else{
							hasHeight = true;
							textureData.height = value;
						}
					}
					else if (valName == "specular"){
						FileBuffer file(value);
						if (!file.Exists()){
							Engine::LogError("Cannot find spec map: " + value, Engine::errorType::TYPE_WARNING);
						}
						else{
							hasSpecular = true;
							textureData.specular = value;
						}
					}
					else if (valName == "illum"){
						FileBuffer file(value);
						if (!file.Exists()){
							Engine::LogError("Cannot find illumination map: " + value, Engine::errorType::TYPE_WARNING);
						}
						else{
							hasIllum = true;
							textureData.illum = value;
						}
					}
				}
			}
			++it;
		}
	}

	if (!hasDiffuse){
		diffuse = new Texture("materials/default.tga");
		hasDiffuse = true;
	}

	bool d = false;
	bool n = false;
	bool s = false;
	bool h = false;
	bool i = false;
	list<UsedMaterial>::iterator Mit = World::sceneTextures.begin();
	while (Mit != World::sceneTextures.end()){
		if (hasDiffuse && Mit->texName == textureData.diffuse){
			diffuse = Mit->tex;
			d = true;
		}
		if (hasNormal && Mit->texName == textureData.normal){
			normal = Mit->tex;
			n = true;
		}
		if (hasSpecular && Mit->texName == textureData.specular){
			specular = Mit->tex;
			s = true;
		}
		if (hasHeight && Mit->texName == textureData.height){
			height = Mit->tex;
			h = true;
		}
		if (hasIllum && Mit->texName == textureData.illum){
			illum = Mit->tex;
			i = true;
		}
		if (++Mit == World::sceneTextures.end()){
			if (hasDiffuse && !d){
				diffuse = new Texture(textureData.diffuse);
				UsedMaterial nMat(diffuse, textureData.diffuse);
				World::sceneTextures.push_back(nMat);
			}
			if (hasNormal && !n){
				normal = new Texture(textureData.normal);
				UsedMaterial nMat(normal, textureData.normal);
				World::sceneTextures.push_back(nMat);
			}
			if (hasSpecular && !s){
				specular = new Texture(textureData.specular);
				UsedMaterial nMat(normal, textureData.specular);
				World::sceneTextures.push_back(nMat);
			}
			if (hasHeight && !h){
				height = new Texture(textureData.height);
				UsedMaterial nMat(normal, textureData.height);
				World::sceneTextures.push_back(nMat);
			}
			if (hasIllum && !i){
				specular = new Texture(textureData.illum);
				UsedMaterial nMat(normal, textureData.illum);
				World::sceneTextures.push_back(nMat);
			}
			break;
		}
		++Mit;
	}

	World::sceneMaterials.push_back(this);
}

void Material::UseMaterial(){
	shader->UseShader();
	glEnable(GL_TEXTURE_2D);
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, diffuse->GetTexture());
	//glActiveTexture(GL_TEXTURE1);
	//glBindTexture(GL_TEXTURE_2D, normal->GetTexture());
	//glActiveTexture(GL_TEXTURE2);
	//glBindTexture(GL_TEXTURE_2D, specular->GetTexture());
	//glActiveTexture(GL_TEXTURE3);
	//glBindTexture(GL_TEXTURE_2D, illum->GetTexture());
	//glActiveTexture(GL_TEXTURE4);
	//glBindTexture(GL_TEXTURE_2D, height->GetTexture());
}

ShaderProgram Material::GetProgram(){
	return *shader;
}
Texture Material::GetDiffuse(){
	return *diffuse;
}

Texture Material::GetNormal(){
	return *normal;
}

Texture Material::GetSpecular(){
	return *specular;
}

Texture Material::GetIllum(){
	return *illum;
}

Texture Material::GetHeight(){
	return *height;
}

void Material::UnBind(){
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, 0);
	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, 0);
	glActiveTexture(GL_TEXTURE2);
	glBindTexture(GL_TEXTURE_2D, 0);
	glActiveTexture(GL_TEXTURE3);
	glBindTexture(GL_TEXTURE_2D, 0);
	glActiveTexture(GL_TEXTURE4);
	glBindTexture(GL_TEXTURE_2D, 0);
}

void Material::SetColour(Colour col){
	colour = col;
}

bool Material::IsTransparent(){
	if (masked){
		return true;
	}
	else if (translucentcy < 1.0f){
		return true;
	}
	return false;
}

Colour Material::GetColour(){
	return colour;
}

float Material::GetTiling(){
	return tilingMultipler;
}